注意:所有文章除特别说明外,转载请注明出处.
第13章 HandlerAdapter
[TOC]
HandlerMapping通过request找到Handler,而HandlerAdapter是具体使用Handler来干活的。每个HandlerAdapter封装了一种Handler的具体使用方法。HandlerAdapter结构中有5个Adapter,其中只有RequestMappingHandlerAdapter有两层,而其它的只有一层,是直接实现HandlerAdapter接口。同时其中的有一个类已经弃用,所以只有4类Adapter,5个类。
提示:在HandlerAdapter的接口中只有三个方法:1.判断是否支持传入Handler。2.使用Handler处理请求。3.获取资源的LastModified值。
13.1 RequestMappingHandlerAdapter
RequestMappingHandlerAdapter适配器是与RequestMappingHandlerMapping配合使用的。
13.1.1 AbstractHandlerMethodAdapter
1.supports()方法
AbstractHandlerMethodAdapter类实现了supports()方法,可以知道支持的Handler是HandlerMethod,同时附加了一个判断方法supportsInternal,这个方法由RequestMappingHandlerAdapter实现,其就是简单地返回true。
@Override
public final boolean supports(Object handler) {
// 判断当前handler是否为HandlerMethod类型,并且判断supportsInternal()方法返回值是否为true,
// 这里supportsInternal()方法是提供给子类实现的一个方法,对于RequestMappingHandlerAdapter
// 而言,其返回值始终是true,因为其只需要处理的handler是HandlerMethod类型的即可
return (handler instanceof HandlerMethod
&& supportsInternal((HandlerMethod) handler));
}
@Override
protected boolean supportsInternal(HandlerMethod handlerMethod) {
// 这里RequestMappingHandlerAdapter只是对supportsInternal()返回true,因为其只需要
// 处理的handler类型是HandlerMethod类型即可
return true;
}
@Override
protected boolean supportsInternal(HandlerMethod handlerMethod) {
// 这里RequestMappingHandlerAdapter只是对supportsInternal()返回true,因为其只需要
// 处理的handler类型是HandlerMethod类型即可
return true;
}
2.handle()方法
在supports()方法判断了所处理的handler是HandlerMethod类型之后,RequestMappingHandlerAdapter将会调用handle()方法处理当前请求。AbstractHandlerMethodAdapter实现了handle方法,就是简单地调用了模板方法handleInternal,handleInternal方法由RequestMappingHandlerAdapter实现。
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
@Override
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);
// 判断当前是否需要支持在同一个session中只能线性地处理请求
if (this.synchronizeOnSession) {
// 获取当前请求的session对象
HttpSession session = request.getSession(false);
if (session != null) {
// 为当前session生成一个唯一的可以用于锁定的key
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
// 对HandlerMethod进行参数等的适配处理,并调用目标handler
mav = invokeHandlerMethod(request, response, handlerMethod);
}
} else {
// 如果当前不存在session,则直接对HandlerMethod进行适配
mav = invokeHandlerMethod(request, response, handlerMethod);
}
} else {
// 如果当前不需要对session进行同步处理,则直接对HandlerMethod进行适配
mav = invokeHandlerMethod(request, response, handlerMethod);
}
// 判断当前请求头中是否包含Cache-Control请求头,如果不包含,则对当前response进行处理,
// 为其设置过期时间
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
// 如果当前SessionAttribute中存在配置的attributes,则为其设置过期时间。
// 这里SessionAttribute主要是通过@SessionAttribute注解生成的
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
} else {
// 如果当前不存在SessionAttributes,则判断当前是否存在Cache-Control设置,
// 如果存在,则按照该设置进行response处理,如果不存在,则设置response中的
// Cache的过期时间为-1,即立即失效
prepareResponse(response);
}
}
return mav;
}
提示:上面的程序作了两部分处理:1.判断当前是否对session进行同步处理,如果需要则进行加锁处理,不需要则直接调用。2.判断请求头是否已经包含Cache-Control请求头,如果不包含则设置Cache立即失效。对HandlerMethod的具体请求是在invokeHandlerMethod()方法中进行的。
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
// 获取容器中全局配置的InitBinder和当前HandlerMethod所对应的Controller中
// 配置的InitBinder,用于进行参数的绑定
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
// 获取容器中全局配置的ModelAttribute和当前当前HandlerMethod所对应的Controller
// 中配置的ModelAttribute,这些配置的方法将会在目标方法调用之前进行调用
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// 将handlerMethod封装为一个ServletInvocableHandlerMethod对象,
// 该对象用于对当前request的整体调用流程进行了封装
ServletInvocableHandlerMethod invocableMethod =
createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
// 设置当前容器中配置的所有ArgumentResolver
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
// 设置当前容器中配置的所有ReturnValueHandler
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
// 将前面创建的WebDataBinderFactory设置到ServletInvocableHandlerMethod中
invocableMethod.setDataBinderFactory(binderFactory);
// 设置ParameterNameDiscoverer,该对象将按照一定的规则获取当前参数的名称
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
// 这里initModel()方法主要作用是调用前面获取到的@ModelAttribute标注的方法,
// 从而达到@ModelAttribute标注的方法能够在目标Handler调用之前调用的目的
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
// 获取当前的AsyncWebRequest,这里AsyncWebRequest的主要作用是用于判断目标
// handler的返回值是否为WebAsyncTask或DefferredResult,如果是这两种中的一种,
// 则说明当前请求的处理应该是异步的。所谓的异步,指的是当前请求会将Controller中
// 封装的业务逻辑放到一个线程池中进行调用,待该调用有返回结果之后再返回到response中。
// 这种处理的优点在于用于请求分发的线程能够解放出来,从而处理更多的请求,只有待目标任务
// 完成之后才会回来将该异步任务的结果返回。
AsyncWebRequest asyncWebRequest = WebAsyncUtils
.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
// 封装异步任务的线程池,request和interceptors到WebAsyncManager中
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
// 这里就是用于判断当前请求是否有异步任务结果的,如果存在,则对异步任务结果进行封装
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer)
asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
if (logger.isDebugEnabled()) {
logger.debug("Found concurrent result value [" + result + "]");
}
// 封装异步任务的处理结果,虽然封装的是一个HandlerMethod,但只是Spring简单的封装
// 的一个Callable对象,该对象中直接将调用结果返回了。这样封装的目的在于能够统一的
// 进行右面的ServletInvocableHandlerMethod.invokeAndHandle()方法的调用
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
// 对请求参数进行处理,调用目标HandlerMethod,并且将返回值封装为一个ModelAndView对象
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
// 对封装的ModelAndView进行处理,主要是判断当前请求是否进行了重定向,如果进行了重定向,
// 还会判断是否需要将FlashAttributes封装到新的请求中
return getModelAndView(mavContainer, modelFactory, webRequest);
} finally {
// 调用request destruction callbacks和对SessionAttributes进行处理
webRequest.requestCompleted();
}
}
提示:RequestMappingHandlerAdapter处理请求的主要流程:1.获取当前容器中使用@InitBinder注解注册的属性转换器。2.获取当前容器中使用@ModelAttribute标注但是没有使用@RequestMapping标注的方法,并且在调用目标方法之前调用这些方法。3.判断目标handler返回值是否使用了WebAsync Task或DefferredResult封装,如果封装了按照异步任务的方式执行。4.处理请求参数,调用目标方法和处理返回值。
private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod)
throws Exception {
// 判断当前缓存中是否缓存了当前bean所需要装配的InitBinder方法,如果存在,则直接从缓存中取,
// 如果不存在,则在当前bean中进行扫描获取
Class<?> handlerType = handlerMethod.getBeanType();
Set<Method> methods = this.initBinderCache.get(handlerType);
if (methods == null) {
// 在当前bean中查找所有标注了@InitBinder注解的方法,这里INIT_BINDER_METHODS就是一个
// 选择器,表示只获取使用@InitBinder标注的方法
methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
this.initBinderCache.put(handlerType, methods);
}
// 这里initBinderAdviceCache是在RequestMappingHandlerAdapter初始化时同步初始化的,
// 其内包含的方法有如下两个特点:①当前方法所在类使用@ControllerAdvice进行标注了;
// ②当前方法使用@InitBinder进行了标注。也就是说其内保存的方法可以理解为是全局类型
// 的参数绑定方法
List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();
this.initBinderAdviceCache.forEach((clazz, methodSet) -> {
// 这里判断的是当前配置的全局类型的InitBinder是否能够应用于当前bean,
// 判断的方式主要在@ControllerAdvice注解中进行了声明,包括通过包名,类所在的包,
// 接口或者注解的形式限定的范围
if (clazz.isApplicableToBeanType(handlerType)) {
Object bean = clazz.resolveBean();
for (Method method : methodSet) {
initBinderMethods.add(createInitBinderMethod(bean, method));
}
}
});
// 这里是将当前HandlerMethod所在bean中的InitBinder添加到需要执行的initBinderMethods中。
// 这里从添加的顺序可以看出,全局类型的InitBinder会在当前bean中的InitBinder之前执行
for (Method method : methods) {
Object bean = handlerMethod.getBean();
initBinderMethods.add(createInitBinderMethod(bean, method));
}
// 将需要执行的InitBinder封装到InitBinderDataBinderFactory中
return createDataBinderFactory(initBinderMethods);
}
13.1.2 RequestMappingHandlerAdapter
1.初始化过程
RequestMappingHandlerAdapter实现了InitializingBean接口,所以它的初始化入口是afterPropertiesSet方法。也就是说 RequestMappingHandlerAdapter 的创建是在 afterPropertiesSet() 方法中实现的。
提示:handler表示Spring处理具体请求的某个Controller方法,即HandlerAdapter表示将当前请求适配到某个Handler的处理器。RequestMappingHandlerAdapter是HandlerAdapter的一个具体实现,主要用于将某个请求适配给@RequestMapping类型的Handler处理。
2.HandlerMapping接口的声明
public interface HandlerAdapter {
// 用于判断当前HandlerAdapter是否能够处理当前请求
boolean supports(Object handler);
// 如果当前HandlerAdapter能够用于适配当前请求,那么就会处理当前请求中
// 诸如参数和返回值等信息,以便能够直接委托给具体的Handler处理
ModelAndView handle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception;
// 获取当前请求的最后更改时间,主要用于供给浏览器判断当前请求是否修改过,
// 从而判断是否可以直接使用之前缓存的结果
long getLastModified(HttpServletRequest request, Object handler);
}
属性:1.argumentResolvers 用于给处理器方法和被@ModelAttribute注解的方法设置参数。2.initBinderArgumentResolvers 用于给被@InitBinder注解的方法设置参数。3.returnValueHandlers 用于将处理器返回值处理成ModelAndView类型。
public void afterPropertiesSet() {
//初始化注释了 @ControllerAdvice 的类的相关属性
initControllerAdviceCache();
if (this.argumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.initBinderArgumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.returnValueHandlers == null) {
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}
13.2 RequestMappingHandlerAdapter 自身结构
13.2.1 创建RequestMappingHandlerAdapter
创建 RequestMappingHandlerAdapter 是在 afterPropertiesSet()方法中实现,内容主要是初始化 argumentResolvers | initBinderArgumentResolvers | returnValueHandlers | @ControllerAdvice注释的类相关的modelAttributeAdviceCache | initBinderAdviceCache | responseBodyAdvice 六个属性。
1.argumentResolvers 给处理器方法和注释了@ModelAttribute的方法设置参数
2.initBinderArgumentResolvers 给注释了@initBinder的方法设置参数
3.returnValueHandlers 将处理器的返回值处理成ModelAndView的类型
4.modelAttributeAdviceCache | initBinderAdviceCache 用于缓存 @ControllerAdvice注释的类里面的注释了@ModelAttribute 和 @InitBinder 的方法。
5.responseBodyAdvice 保存 ResponseBodyAdvice 接口,可以修改返回的 ResponseBody 的类。
注意:这些属性都是复数形式,可以有多个,使用的时候根据顺序调用即可。所以初始化时的顺序很重要。
13.2.2 RequestMappingHandlerAdapter之用
RequestMappingHandlerAdapter 的处理请求入口方法是handleInternal。
protected final ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
//1.判断Handler是否有 @SessionAttribute 注释的参数
if (this.getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
this.checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
} else {
this.checkAndPrepare(request, response, true);
}
//看代码应该是从session中获取一些信息,然后初始化header等信息
//这块就是根据需要是否进行同步操作
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized(mutex) {
return this.invokeHandleMethod(request, response, handlerMethod);
}
}
}
//正式进入执行环节
return this.invokeHandleMethod(request, response, handlerMethod);
}
提示:这里关键的方法就是:checkAndPrepare()方法和invokeHandleMethod()方法。
13.3 ModelAndViewContainer
ModelAndViewContainer承担着整个请求过程中数据的传递工作。除了保存Model和View之外还有其它的功能。它里面包含的属性有:
1.view 视图,Object类型
2.defaultModel:默认使用的Model
3.redirectModel:redirect类型的Model
4.sessionStatus:用于设置SessionAttribute使用完的标志
5.ignoreDefaultModelOnRedirect:如果为true则在处理器返回redirect视图时一定不使用defaultModel
6.redirectModelScenario:处理器返回redirect视图的标志
7.requestHandled:请求是否已经处理完成的标志
13.4 SessionAttributesHandler 和 SessionAttributeStore
SessionAttributesHandler 作为处理 @SessionAttributes注释的参数,其只做宏观的事情,如:哪个Handler都可以缓存哪些参数,某个参数在当前的SessionAttribute中是否存在等。具体的存储工作交给SessionAttributeStore做,
13.5 ModelFactory
ModelFactory是用来维护Model的。两个功能:1. 初始化Model。2.处理器执行后将Model中相应参数更新到SessionAttribute。
13.5.1 初始化Model
13.6 ServletInvocableHandlerMethod
是一种HandlerMethod,只是增加了方法执行的功能,相应地增加了参数解析、返回值处理等功能。
13.6.1 HandlerMethod
13.7 HandlerMethodArgumentResolver
是为处理器解析参数的,主要用在InvocableHandlerMethod中。每个Resolver对应一种类型的参数。
13.8 HandlerMethodReturnValueHandler
该处理方法用在ServletInvocableHandlerMethod中,功能:1. 将相应参数添加到Model。2. 设置View。3. 如果请求已经处理完则设置ModelAndViewContainer的requestHandled为true。
###